#include <stdio.h>
#include <stdlib.h>
#include "oper.h"
#include "oom.h"
#include "dtypes.h"
#include "mpi.h"

/*****************************************
 * \author James Edmondson
 * \brief Performs max reduction operation for integer vectors.
 * ******************************************/

void int_max(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;

	for( i = 0; i < *len; i++ )
	{
		if( incoming[i] > result[i] )
		{
			result[i] = incoming[i];
		}
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs max reduction operation for float vectors.
 * ******************************************/

void float_max(void * invec, void * inoutvec, int * len)
{
	float * incoming = invec;
	float * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		if( incoming[i] > result[i] )
		{
			result[i] = incoming[i];
		}
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs min reduction operation for integer vectors.
 * ******************************************/

void int_min(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		if( incoming[i] < result[i] )
		{
			result[i] = incoming[i];
		}
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs min reduction operation for float vectors.
 * ******************************************/

void float_min(void * invec, void * inoutvec, int * len)
{
	float * incoming = invec;
	float * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		if( incoming[i] < result[i] )
		{
			result[i] = incoming[i];
		}
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs sum reduction operation for integer vectors.
 * ******************************************/

void int_sum(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] + result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs sum reduction operation for float vectors.
 * ******************************************/

void float_sum(void * invec, void * inoutvec, int * len)
{
	float * incoming = invec;
	float * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] + result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs product reduction operation for integer vectors.
 * ******************************************/

void int_prod(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] * result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs product reduction operation for float vectors.
 * ******************************************/

void float_prod(void * invec, void * inoutvec, int * len)
{
	float * incoming = invec;
	float * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] * result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical and reduction operation for int vectors.
 * ******************************************/

void int_land(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] && result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical or reduction operation for int vectors.
 * ******************************************/

void int_lor(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] || result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical exclusive or reduction operation for int vectors.
 * ******************************************/

void int_lxor(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		/* convert each variable into a binary choice (true / false)
		 * then perform comparison. Truth table should work out.
		 *
		 *    0 1
		 *  0 F	T
		 *  1 T F */
		
		result[i] = !!incoming[i] != !!result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary and reduction operation for integer vectors.
 * ******************************************/

void int_band(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] & result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary and reduction operation for byte vectors.
 * ******************************************/

void byte_band(void * invec, void * inoutvec, int * len)
{
	char * incoming = invec;
	char * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] & result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary or reduction operation for integer vectors.
 * ******************************************/

void int_bor(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] | result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary or reduction operation for byte vectors.
 * ******************************************/

void byte_bor(void * invec, void * inoutvec, int * len)
{
	char * incoming = invec;
	char * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] | result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary exclusive or reduction operation for integer vectors.
 * ******************************************/

void int_bxor(void * invec, void * inoutvec, int * len)
{
	int * incoming = invec;
	int * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] ^ result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary exclusive or reduction operation for byte vectors.
 * ******************************************/

void byte_bxor(void * invec, void * inoutvec, int * len)
{
	char * incoming = invec;
	char * result = inoutvec;
	int i = 0;
	
	for( i = 0; i < *len; i++ )
	{
		result[i] = incoming[i] ^ result[i];
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs max reduction operation.
 * ******************************************/

void max(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_max(invec,inoutvec,len);
	}
	else if( *datatype == MPI_FLOAT )
	{
		float_max(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs min reduction operation.
 * ******************************************/

void min(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_min(invec,inoutvec,len);
	}
	else if( *datatype == MPI_FLOAT )
	{
		float_min(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs sum reduction operation.
 * ******************************************/

void sum(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_sum(invec,inoutvec,len);
	}
	else if( *datatype == MPI_FLOAT )
	{
		float_sum(invec,inoutvec,len);
	}
}

void prod(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_prod(invec,inoutvec,len);
	}
	else if( *datatype == MPI_FLOAT )
	{
		float_prod(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical and reduction operation.
 * ******************************************/

void land(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_land(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical or reduction operation.
 * ******************************************/

void lor(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_lor(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs logical exclusive or reduction operation.
 * ******************************************/

void lxor(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_lxor(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary and reduction operation.
 * ******************************************/

void band(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_band(invec,inoutvec,len);
	}
	else if( *datatype == MPI_BYTE )
	{
		byte_band(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary or reduction operation.
 * ******************************************/

void bor(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_bor(invec,inoutvec,len);
	}
	else if( *datatype == MPI_BYTE )
	{
		byte_bor(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Performs binary exclusive or reduction operation.
 * ******************************************/

void bxor(void * invec, void * inoutvec, int * len, MPI_Datatype * datatype)
{
	if( *datatype == MPI_INT )
	{
		int_bxor(invec,inoutvec,len);
	}
	else if( *datatype == MPI_BYTE )
	{
		byte_bxor(invec,inoutvec,len);
	}
}

/*****************************************
 * \author James Edmondson
 * \brief Register all predefined reduction operations.
 *        This is only called once, during MPI_Init (actually
 *        called during CM_Init).
 * ******************************************/

void register_reserved_ops()
{
	OOM_op_create_explicit(max,1,MPI_MAX);
	OOM_op_create_explicit(min,1,MPI_MIN);
	OOM_op_create_explicit(sum,1,MPI_SUM);
	OOM_op_create_explicit(prod,1,MPI_PROD);
	OOM_op_create_explicit(land,1,MPI_LAND);
	OOM_op_create_explicit(lor,1,MPI_LOR);
	OOM_op_create_explicit(lxor,1,MPI_LXOR);
	OOM_op_create_explicit(band,1,MPI_BAND);
	OOM_op_create_explicit(bor,1,MPI_BOR);
	OOM_op_create_explicit(bxor,1,MPI_BXOR);
}

